iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
0
Modern Web

今晚,我想來點Blazor系列 第 27

Day 27:Blazor x Chart.js

  • 分享至 

  • xImage
  •  

Chart.js是一款open source的圖表製作library,支援多種圖表,包括Pie chart、Bar chart、line chart...等等,也有動畫效果,可以製作出精美的報表。

今天我們要建立一個Blazor WebAssembly專案,這個專案會串接一個新冠肺炎的Api,並使用Chart.js呈現近60天的確診人數折線圖,那我們就開始吧!

接下來主要分3部分進行:

  1. 串接Api的Service
  2. 設定Chart.js和程式碼
  3. 在index.razor取得資料和圖表並顯示在頁面上

串接Api的Service

Api部份,我們可以用https://covid19api.com/ 這個網站的Api來取得資料,一般資料是free的,如果有更多資料的需求再訂閱付費,我們可以到他的文件看一下有哪些Api可用。

首先建立Blazor WebAssembly專案,取消勾選Asp.net core hosted
https://ithelp.ithome.com.tw/upload/images/20201011/20130058S3osauA09r.jpg

建立完專案後,新增Service和Model資料夾,Model中加入json要對應的類別,Service資料夾加入COVID19Service。
COVID19Service有兩個方法:
GetCountriesAsync:取得index.razor的國家下拉選單資料
GetCountrySummaryAsync:傳入CountryCode,取得某國的確診、死亡等等人數

public class COVID19Service : ICOVID19Service
    {
        private readonly HttpClient httpClient;

        private string baseurl { get; set; } = "https://api.covid19api.com";

        public COVID19Service(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        /// <summary>
        /// 取得國家清單
        /// </summary>
        /// <returns></returns>
        public async Task<List<CountryModel>> GetCountriesAsync()
        {
            List<CountryModel> countries = new List<CountryModel>();
            var response = await httpClient.GetAsync($"{baseurl}/countries");

            if (response.IsSuccessStatusCode)
            {
                var contentString = await response.Content.ReadAsStringAsync();
                countries = JsonConvert.DeserializeObject<List<CountryModel>>(contentString);
            }

            return countries;
        }

        /// <summary>
        /// 取得確診、死亡等等人數資料
        /// </summary>
        /// <param name="countryCode"></param>
        /// <returns></returns>
        public async Task<List<CountrySummary>> GetCountrySummaryAsync(string countryCode)
        {
            List<CountrySummary> countrySummary = new List<CountrySummary>();

            var respones = await httpClient.GetAsync($"{baseurl}/country/{countryCode}");
            if (respones.IsSuccessStatusCode)
            {
                var contentString = await respones.Content.ReadAsStringAsync();
                countrySummary = JsonConvert.DeserializeObject<List<CountrySummary>>(contentString);
            }

            return countrySummary.TakeLast(60).ToList();
        }
    }

在Program.cs註冊COVID19Service

builder.Services.AddScoped<ICOVID19Service, COVID19Service>();

設定Chart.js和繪製圖表程式碼

使用cdn引入chart.js。可從https://cdnjs.com/libraries/Chart.js 取得cdn連結
再來把cdn script拉進wwwroot/index.html

<!DOCTYPE html>
<html>
//略....
<body>
    <app>Loading...</app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">?</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
    <script src="script/MyChart.js"></script>
</body>
</html>

另外還拉了一個MyChart.js進去,這是我們自己建立的js檔,待會會在裡面寫產生圖表的程式碼

接下來到index.razor,這邊增加一個canvas標籤,產生的圖表會放到這個canvas內

<canvas id="myChart" width="800" height="300"></canvas>

在MyChart.js建立圖表資料

function DrawLineChart(summaryListlist) {
    //建立圖表資料
    var datachart = {
        labels: [],
        datasets: [{
            label: '確診人數',
            data: [],
            borderColor: '#FF5376',
            fill: false
        }]
    };
    
    //將日期與人數push到labels和data陣列中
    for (var i = 0; i < summaryListlist.length; i++) {
        datachart.labels.push(summaryListlist[i].shortDate);
        datachart.datasets[0].data.push(summaryListlist[i].confirmed);
    }
    
    //繪製圖表
    var ctx = document.getElementById("myChart").getContext("2d");
    var chart = new Chart(ctx, {
        type: 'line', //圖表類型
        data: datachart,
    })
}

chart.js的官方文件還有許多設定可以套用,讓我們的圖表可以更美觀更友善

在index.razor取得資料和圖表並顯示在頁面上

在畫面上會準備一個國家的下拉選單給user選取要看哪一國的資料,所以下拉選單部分會透過上述建立的COVID19Service取的國家資料後在塞到Select標籤中

@page "/"
@inject ICOVID19Service COVID19Service

<h2>COVID19 Chart</h2>
<hr />

@if (countries != null)
{
    <div class="form-group">
        <label for="countrySelect" class="font-weight-bold">國家</label>
        <select @onchange="ChangeCountryAsync" class="form-control" id="countrySelect">
            <option>--請選擇--</option>

            @foreach (var item in countries)
            {
                <option value="@item.Slug" selected="@(item.Slug == DefaultCountry)">@item.Country</option>
            }
        </select>
    </div>

    //顯示chart的canvas標籤
    <canvas id="myChart" width="800" height="300"></canvas>
}
else
{
    //還在讀取資料時,顯示loading gif
    <img src="https://media.giphy.com/media/3oEjI6SIIHBdRxXI40/giphy.gif" />
}

@code{
public string DefaultCountry { get; set; } = "taiwan";
public List<CountryModel> countries;

protected override async Task OnInitializedAsync()
    {
        countries = await COVID19Service.GetCountriesAsync();   
    }   
}

在一進入index.razor,我希望顯示的是台灣的確診資料,因此在OnInitializedAsync方法取得下拉選單然後用一個預設為taiwan的DefaultCountry屬性,讓下拉選單預設選到taiwan

傳入countryCode取確診資料並透過IJSRuntime物件繪製圖表

@inject IJSRuntime js

@code{
private async Task BuildChartAsync(string selectedValue)
    {        
        summaryList = await COVID19Service.GetCountrySummaryAsync(selectedValue);
        summaryList.ForEach(x => x.ShortDate = $"{x.Date.Month}/{x.Date.Day}");

        await js.InvokeVoidAsync("DrawLineChart", summaryList);
    }
}

在OnInitializedAsync也加入BuildChartAsync

protected override async Task OnInitializedAsync()
    {
        countries = await COVID19Service.GetCountriesAsync();
        BuildChartAsync(DefaultCountry);
    }

選取國家時,處理onchange事件的ChangeCountryAsync方法中,使用ChangeEventArgs參數取得選擇的value,在傳入剛剛建立的BuildChartAsync方法

public async Task ChangeCountryAsync(ChangeEventArgs e)
    {
        if (e.Value != null)
        {
            await BuildChartAsync(e.Value.ToString());
        }
    }

最後完成的結果

程式碼可參考:https://github.com/CircleLin/COVID19_Chart


上一篇
Day 26:Blazor WebAssembly 上傳檔案
下一篇
Day 28:元件的單元測試
系列文
今晚,我想來點Blazor30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
WT
iT邦新手 5 級 ‧ 2020-11-05 11:53:09

向您請教一下,我下載這個範例程式碼,我搜尋了5.6次後,再次搜尋他會把我前面搜尋過的結果圖表,全部都在跑一次,是什麼地方造成的錯誤嗎?

我要留言

立即登入留言